SecurityGroupに対して自分で許可したIPを複数のAWSアカウント・リージョンから探すためのPythonを書いた
サーモン大好き横山です。
複数の共用AWSアカウントがあり、この度諸事情で離れることになりました。その際ぬくもりのある手作業にて踏み台サーバに対してSecurityGroupでIPを許可していましたので、Ruleを削除しようとしました。しかし、恥ずかしながらどこのアカウント、どこのリージョンにIPを許可したかを覚えていませんでした。そこで、今回許可したIPと自分で設定したSecurity Group RuleのDescriptionから探し出すPythonコードを書きました。
前提
Pythonのライブラリはboto3を使いますので、boto3をインストールしておいてください。比較的新しいAPI( describe_security_group_rules
)を使うので、新しいバージョンを使います。
$ pip3 install boto3
あと、探したい環境のAWSCLIの複数Profileの設定をお願いします。(参考: 名前付きプロファイル - AWS Command Line Interface ユーザーガイド)
[env1] aws_access_key_id=AKIAxxxyyy aws_secret_access_key=salmonsalmonsalmonsalmon [env2] aws_access_key_id=AKIAzzzppp aws_secret_access_key=ikuraikuraikuraikuraikura [env3] aws_access_key_id=AKIAaaabbb aws_secret_access_key=tarafugutarafugutara
あと、これが一番重要なんですが、自分で許可したIPやSecurityGroupRuleのDescriptionを覚えていることです。
処理内容
boto3で各AWSアカウント・リージョンのec2を操作するクライアントを作り、 describe_security_group_rules
を取得し、cidrの一致 もしくは descriptionの部分一致するものを探しコンソールに出力する。
コード
変数 search_cidrs
に探したいCIDR IPを、 search_descriptions
にSecurity Group RuleのDescriptionに書いてあった文言一部が一致するSecurity Group Ruleを探し出して表示するPythonコードです。
from typing import List from boto3.session import Session def search_cidr_in_sg_rule(sg_rule, cidr) -> bool: """ security group ruleの内容とcidrが一致するか確認する """ if not "CidrIpv4" in sg_rule: return False return sg_rule["CidrIpv4"] == cidr def search_description_in_sg_rule(sg_rule, search_description) -> bool: """ security group ruleの内容とdescriptionが部分一致するか確認する """ if not "Description" in sg_rule: return False return search_description in sg_rule["Description"] def search_sg_rules( ec2_client, search_cidrs: List = [], search_descriptions: List = [] ) -> List: """ 検索cidr、descriptionに引っかかるsecurity group ruleの一覧を取得 """ page_iter = ec2_client.get_paginator("describe_security_group_rules") sg_rules = [] for page in page_iter.paginate(PaginationConfig=dict(PageSize=1000)): sg_rules.extend(page["SecurityGroupRules"]) hit_sg_rules = {} for sg_rule in sg_rules: for ip in search_cidrs: if search_cidr_in_sg_rule(sg_rule, ip): sg_rule_id = sg_rule["SecurityGroupRuleId"] if sg_rule_id not in hit_sg_rules: hit_sg_rules[sg_rule_id] = sg_rule for word in search_descriptions: if search_description_in_sg_rule(sg_rule, word): sg_rule_id = sg_rule["SecurityGroupRuleId"] if sg_rule_id not in hit_sg_rules: hit_sg_rules[sg_rule_id] = sg_rule return hit_sg_rules.values() def display_sg_group_rule(sg_rules) -> None: """ ターミナルに整形して表示する """ for sg_rule in sg_rules: print( f"GroupId: {sg_rule['GroupId']:<20}, Description: {sg_rule.get('Description', ''):<30}, ", end="", ) print( f"Cidr: {sg_rule['CidrIpv4']:<18}, Port: {sg_rule['FromPort']} -> {sg_rule['ToPort']}" ) if __name__ == "__main__": # 検索IP search_cidrs = [ "182.xxx.xxx.zzz/32", "114.xxx.xxx.yyy/32", "160.xxx.xxx.www/32", ] # 検索Description search_descriptions = ["yoko"] # AWSCLIのプロファイル名 profiles = [ "env1", "env2", "env3", ] # 全リージョン取得 # 参考: https://dev.classmethod.jp/articles/iterate-all-region-with-python-boto3/ regions = [ region["RegionName"] for region in Session(profile_name=profiles[0]) .client("ec2", region_name="ap-northeast-1") .describe_regions()["Regions"] ] ec2_clients = { profile: { region: Session(profile_name=profile).client("ec2", region_name=region) for region in regions } for profile in profiles } for profile_name, ec2_client_regions in ec2_clients.items(): for region_name, ec2_client in ec2_client_regions.items(): sg_rules = search_sg_rules(ec2_client, search_cidrs, search_descriptions) if sg_rules: print(f"=== {profile_name}, {region_name} ===") display_sg_group_rule(sg_rules)
結果
search_cidrs
と search_descriptions
のいずれかに該当するSecurity Group Idと Security Group Rule IdのDescription、Cidr、Portを出力します
=== env1, ap-northeast-1 === GroupId: sg-xxxxxxxx , Description: yokoyama.fumihito , Cidr: 160.xxx.xxx.www/32 , Port: 22 -> 22 GroupId: sg-xxxxxxxx , Description: yokoyama.fumihito-tmp , Cidr: 182.xxx.xxx.zzz/32 , Port: 22 -> 22 GroupId: sg-yyyyyyyy , Description: home , Cidr: 114.xxx.xxx.yyy/32 , Port: 22 -> 22 GroupId: sg-zzzzzzzzzzzzzzzzz, Description: yoko-home , Cidr: 114.xxx.xxx.yyy/32 , Port: 22 -> 22 GroupId: sg-xxxxxxxx , Description: yokoyama.fumihito , Cidr: 114.xxx.xxx.yyy/32 , Port: 22 -> 22 === env1, us-west-2 === GroupId: sg-ssssssss , Description: yokoyama , Cidr: 114.xxx.xxx.yyy/32 , Port: 22 -> 22 === env2, ap-northeast-1 === GroupId: sg-tttttttt , Description: yoko home , Cidr: 114.xxx.xxx.yyy/32 , Port: 22 -> 22 GroupId: sg-tttttttt , Description: tmp-deploy(yoko) , Cidr: 138.xxx.xxx.qqq/32 , Port: 22 -> 22 === env3, ap-northeast-1 === GroupId: sg-pppppppp , Description: yoko home , Cidr: 114.xxx.xxx.yyy/32 , Port: 22 -> 22
まとめ
今回はRuleを削除するのが目的でしたが、削除を含めると誤って削除した場合大変なことになるので、今回は一覧を作成し該当するRuleをマネージドコンソールから削除する対応を行いました。
SecurityGroupに穴を開ける際は、CIDR IPをメモっておくか、Descriptionに決まった文言をつけて作っておけば後で削除し忘れたときにどのSecurityGroupに穴を開けたかをこのPythonコードで調べることができます。
この記事がだれかのお役に立てば幸いです。